package org.softmed.action;

import java.lang.reflect.Method;
import java.util.List;

import org.softmed.action.states.State;
import org.softmed.action.states.custom.ExecutingState;
import org.softmed.action.states.custom.NormalState;
import org.softmed.action.workarea.WorkArea;

public class Action implements Cloneable {

	public static final String TESTING = "TESTING";

	public static final String SETUP = "SETUP";

	public static final String ASSERTING = "ASSERTING";

	public static final String PREPARING = "PREPARING";

	public static final String PREPARATION_FAILURE = "PREPARATION_FAILURE";

	public static final String EXECUTING = "EXECUTING";

	public static final String EXECUTION_SUCESS = "EXECUTION_SUCESS";

	public static final String EXECUTION_FAILURE = "EXECUTION_FAILURE";

	public static final String DISPOSING = "DISPOSING";

	protected WorkArea workArea = null;

	NormalState test = new NormalState(TESTING);
	NormalState setup = new NormalState(SETUP);
	NormalState asserting = new NormalState(ASSERTING);

	NormalState preparing = new NormalState(PREPARING);

	NormalState executing = new NormalState(EXECUTING);

	NormalState preparationFailure = new NormalState(PREPARATION_FAILURE);
	NormalState executionSuccess = new NormalState(EXECUTION_SUCESS);
	NormalState executionFailure = new NormalState(EXECUTION_FAILURE);
	NormalState disposing = new NormalState(DISPOSING);

	// private State currentState;

	public Action() {

		setup.setFailure(preparationFailure);
		asserting.setFailure(preparationFailure);

		preparing.addStep(test);
		preparing.addStep(setup);
		preparing.addStep(asserting);

		preparing.setSuccess(executing);

		executing.setFailure(executionFailure);
		executing.setSuccess(executionSuccess);

		preparationFailure.setSuccess(disposing);
		preparationFailure.setFailure(disposing);

		executionSuccess.setSuccess(disposing);
		executionSuccess.setFailure(disposing);

		executionFailure.setSuccess(disposing);
		executionFailure.setFailure(disposing);

		// preparationFailure.setStateChangeListener(this);
		// executionSuccess.setStateChangeListener(this);
		// executionFailure.setStateChangeListener(this);
		// executing.setStateChangeListener(this);
		// disposing.setStateChangeListener(this);
		// preparing.setStateChangeListener(this);
		// setup.setStateChangeListener(this);
		// asserting.setStateChangeListener(this);
		// test.setStateChangeListener(this);

	}

	public Action clone() {
		try {
			Action clone = new Action();
			cloneStates(test, clone.getTest());
			cloneStates(setup, clone.getSetup());
			cloneStates(asserting, clone.getAsserting());
			cloneStates(executing, clone.getExecuting());
			cloneStates(executionFailure, clone.getExecutionFailure());
			cloneStates(executionSuccess, clone.getExecutionSuccess());
			cloneStates(preparationFailure, clone.getPreparationFailure());
			cloneStates(disposing, clone.getDisposing());
			clone.setWorkArea(new WorkArea());
			return clone;

		} catch (Throwable t) {
			throw new RuntimeException(t);
		}

	}

	private void cloneStates(State original, State copy) throws Throwable {
		List<ActionStep> steps = original.getActionSteps();
		for (ActionStep actionStep : steps) {
			if (actionStep instanceof Cloneable) {
				Method cloneMethod = actionStep.getClass().getMethod("clone",
						null);
				ActionStep clone = (ActionStep) cloneMethod.invoke(actionStep,
						null);
				copy.addStep(clone);
			} else
				copy.addStep(actionStep.getClass().newInstance());
		}
	}

	public void run() throws Throwable {
		preparing.execute();
	}

	// @Override
	// public void stateChanged(State newState) {
	// currentState = newState;
	// // System.out.println(newState.getName());
	// }

	public NormalState getTest() {
		return test;
	}

	public NormalState getSetup() {
		return setup;
	}

	public NormalState getAsserting() {
		return asserting;
	}

	public NormalState getExecuting() {
		return executing;
	}

	public NormalState getPreparationFailure() {
		return preparationFailure;
	}

	public NormalState getExecutionSuccess() {
		return executionSuccess;
	}

	public NormalState getExecutionFailure() {
		return executionFailure;
	}

	public NormalState getDisposing() {
		return disposing;
	}

	public WorkArea getWorkArea() {
		return workArea;
	}

	public void setWorkArea(WorkArea workArea) {
		this.workArea = workArea;

		preparationFailure.setWorkArea(workArea);
		executionSuccess.setWorkArea(workArea);
		executionFailure.setWorkArea(workArea);
		executing.setWorkArea(workArea);
		disposing.setWorkArea(workArea);
		preparing.setWorkArea(workArea);
		setup.setWorkArea(workArea);
		asserting.setWorkArea(workArea);
		test.setWorkArea(workArea);
	}
}
